<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>沉浸式 Markdown 编辑器 | Electron × 鸿蒙</title>
    <!-- 页面仅保留沉浸式 Markdown 编辑器。移除平台信息、文件选择与文件详情等模块。 -->
    <style>
      :root {
        color-scheme: light dark;
      }
      body {
        margin: 0;
        font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
        background: #0f172a;
        color: #e5e7eb;
        display: grid;
        place-items: center;
        min-height: 100vh;
      }
      .card {
        width: min(720px, 92vw);
        background: rgba(255,255,255,0.06);
        backdrop-filter: blur(6px);
        border-radius: 16px;
        padding: 24px 28px;
        box-shadow: 0 10px 30px rgba(0,0,0,0.35);
        border: 1px solid rgba(255,255,255,0.12);
      }
      h1 {
        margin: 0 0 12px;
        font-size: 22px;
      }
      .desc {
        margin: 0 0 18px;
        color: #cbd5e1;
        font-size: 14px;
      }
      .list {
        display: grid;
        gap: 10px;
      }
      .item {
        background: rgba(255,255,255,0.08);
        border: 1px solid rgba(255,255,255,0.14);
        padding: 12px 14px;
        border-radius: 10px;
      }
      .k {
        color: #93c5fd;
      }
      code {
        color: #facc15;
        font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
      }
      footer {
        margin-top: 16px;
        font-size: 12px;
        color: #94a3b8;
      }
    </style>
  </head>
  <body>
    <!-- 沉浸式布局：仅保留 Markdown 编辑器（左编辑，右预览），填满视口高度 -->
    <main class="card" aria-label="沉浸式 Markdown 编辑器">
      <h1 id="title">沉浸式 Markdown 编辑器</h1>
      <p class="desc">移除其他模块，仅保留编辑与实时预览（适配鸿蒙容器）。</p>
      <section class="list" aria-label="Markdown 编辑器">
        <div class="item">
          <!--
            说明：
            - 使用 textarea 保证光标与滚动表现稳定；
            - 支持回车自动缩进与列表/引用延续；Tab/Shift+Tab 缩进与反缩进；
            - 右侧实时预览，双向滚动联动（比例同步），实现所见即所得；
            - 无第三方库，保证在鸿蒙容器的安全上下文隔离下可运行。
          -->
          <div id="md-panels" style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:8px;align-items:start;">
            <div style="display:flex;flex-direction:column;gap:6px;">
              <label for="md-editor" style="color:#cbd5e1;font-size:12px;">编辑区</label>
              <textarea id="md-editor" rows="18" spellcheck="false" placeholder="在此输入 Markdown（支持标题、列表、引用、代码块等）"
                style="width:100%;resize:vertical;border-radius:8px;border:1px solid rgba(255,255,255,0.25);background:rgba(255,255,255,0.08);color:#e5e7eb;padding:10px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.6;min-height:360px;"></textarea>
              <div id="md-editor-status" style="color:#94a3b8;font-size:12px;">光标：- | 行：- 列：-</div>
            </div>
            <div style="display:flex;flex-direction:column;gap:6px;">
              <div style="color:#cbd5e1;font-size:12px;">实时预览</div>
              <div id="md-preview" aria-label="实时预览" style="min-height:360px;max-height:60vh;overflow:auto;border-radius:8px;border:1px solid rgba(255,255,255,0.25);background:rgba(255,255,255,0.06);padding:10px;"></div>
            </div>
          </div>
        </div>
      </section>
    </main>

    <script>
      // 仅初始化编辑器与预览，移除平台信息与文件选择逻辑
      document.addEventListener('DOMContentLoaded', () => {
        const mdEditor = document.getElementById('md-editor');
        const mdStatus = document.getElementById('md-editor-status');
        const mdPreview = document.getElementById('md-preview');

        // Markdown 渲染工具：轻量实现常见语法，确保在 contextIsolation 下无需外部库即可使用
        // 说明：先进行 HTML 转义，再按块级/行级规则生成安全 HTML；链接仅允许 http/https。
        const escapeHtml = (s) => String(s)
          .replace(/&/g, '&amp;')
          .replace(/</g, '&lt;')
          .replace(/>/g, '&gt;');

        const applyInline = (text) => {
          // 加粗 **text**
          text = text.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
          // 斜体 *text*
          text = text.replace(/(^|\W)\*(.+?)\*(?=\W|$)/g, '$1<em>$2</em>');
          // 行内代码 `code`
          text = text.replace(/`([^`]+)`/g, '<code>$1</code>');
          // 链接 [text](https://...)
          text = text.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
          return text;
        };

        const renderMarkdown = (md) => {
          // 统一换行并转义
          let src = escapeHtml(md.replace(/\r\n/g, '\n'));
          // 代码块 ``` ... ```
          src = src.replace(/```([\s\S]*?)```/g, (m, code) => {
            return `<pre style="margin-top:8px;background:rgba(255,255,255,0.08);padding:8px;border-radius:6px;overflow:auto;"><code>${code}</code></pre>`;
          });
          // 逐行处理块级元素（标题、引用、列表、段落、分隔线）
          const lines = src.split('\n');
          let html = '';
          let inUl = false, inOl = false;
          const closeLists = () => { if (inUl) { html += '</ul>'; inUl = false; } if (inOl) { html += '</ol>'; inOl = false; } };
          for (const line of lines) {
            // 水平分隔线
            if (/^\s*(?:-{3,}|_{3,}|\*{3,})\s*$/.test(line)) { closeLists(); html += '<hr />'; continue; }
            // 标题 # .. ######
            const hMatch = line.match(/^(#{1,6})\s+(.*)$/);
            if (hMatch) {
              closeLists();
              const level = hMatch[1].length;
              html += `<h${level}>${applyInline(hMatch[2])}</h${level}>`;
              continue;
            }
            // 引用 > text
            const bqMatch = line.match(/^>\s+(.*)$/);
            if (bqMatch) { closeLists(); html += `<blockquote style="margin:8px 0;padding:6px 8px;border-left:3px solid #93c5fd;background:rgba(255,255,255,0.06);">${applyInline(bqMatch[1])}</blockquote>`; continue; }
            // 无序列表 - / *
            let m;
            if ((m = line.match(/^\s*[-*]\s+(.*)$/))) {
              if (!inUl) { closeLists(); html += '<ul style="margin:8px 0 8px 18px;">'; inUl = true; }
              html += `<li>${applyInline(m[1])}</li>`;
              continue;
            }
            // 有序列表 1. text
            if ((m = line.match(/^\s*\d+\.\s+(.*)$/))) {
              if (!inOl) { closeLists(); html += '<ol style="margin:8px 0 8px 18px;">'; inOl = true; }
              html += `<li>${applyInline(m[1])}</li>`;
              continue;
            }
            // 空行
            if (line.trim() === '') { closeLists(); html += '<br />'; continue; }
            // 普通段落
            closeLists();
            html += `<p>${applyInline(line)}</p>`;
          }
          closeLists();
          return html;
        };

        const isMarkdownInfo = (info) => {
          const name = (info.name || '').toLowerCase();
          const ext = (info.ext || '').toLowerCase();
          const byExt = ext === 'md' || ext === 'markdown' || /\.md(?:own)?$/.test(name);
          const byHeuristic = !byExt && typeof info.preview === 'string' && /^(#{1,6}\s+|>\s+|[-*]\s+|\d+\.\s+)/.test(info.preview.trim());
          return byExt || byHeuristic;
        };

        // 注：已移除文件选择列表渲染

        // ---------- Markdown 实时编辑区逻辑 ----------
        // 工具：获取当前光标所在行的起止位置与文本
        const getLineInfo = (el) => {
          const value = el.value;
          const pos = el.selectionStart;
          const start = value.lastIndexOf('\n', pos - 1) + 1; // 行起点（若无换行则 0）
          // 行尾：到下一个换行或末尾
          const endNL = value.indexOf('\n', pos);
          const end = endNL === -1 ? value.length : endNL;
          return { start, end, text: value.slice(start, end), cursor: pos };
        };

        // 更新状态栏：行列与光标位置
        const updateMdStatus = () => {
          if (!mdEditor || !mdStatus) return;
          const value = mdEditor.value;
          const pos = mdEditor.selectionStart;
          const lines = value.slice(0, pos).split('\n');
          const line = lines.length; // 当前行号（1-based）
          const col = lines[lines.length - 1].length + 1; // 当前列号（1-based）
          mdStatus.textContent = `光标：${pos} | 行：${line} 列：${col}`;
        };

        // 插入文本片段并维护光标与滚动位置
        const insertAtCursor = (el, before, after = '') => {
          const prevScroll = el.scrollTop; // 记录滚动位置，避免插入导致跳动
          const start = el.selectionStart;
          const end = el.selectionEnd;
          const value = el.value;
          el.value = value.slice(0, start) + before + after + value.slice(end);
          const newPos = start + before.length;
          el.selectionStart = el.selectionEnd = newPos;
          el.scrollTop = prevScroll;
          updateMdStatus();
        };

        // 回车：自动缩进 + 列表/引用前缀延续
        const handleEnter = (e) => {
          if (e.key !== 'Enter' || e.isComposing) return;
          e.preventDefault();
          const { text } = getLineInfo(mdEditor);
          const indentMatch = text.match(/^(\s+)/);
          const indent = indentMatch ? indentMatch[1] : '';
          let prefix = '';
          // 引用 >
          if (/^\s*>\s+/.test(text)) prefix = '> ';
          // 无序列表 - 或 *
          else if (/^\s*[-*]\s+/.test(text)) prefix = text.match(/^\s*([-*])\s+/)[1] + ' ';
          // 有序列表 1. / 2.
          else if (/^\s*\d+\.\s+/.test(text)) {
            const m = text.match(/^\s*(\d+)\.\s+/);
            prefix = (m ? m[1] : '1') + '. ';
          }
          insertAtCursor(mdEditor, '\n' + indent + prefix);
        };

        // Tab/Shift+Tab：缩进与反缩进（支持多行选择）
        const INDENT = '  '; // 两个空格作为基本缩进
        const handleTab = (e) => {
          if (e.key !== 'Tab' || e.isComposing) return;
          e.preventDefault();
          const start = mdEditor.selectionStart;
          const end = mdEditor.selectionEnd;
          const value = mdEditor.value;
          const sel = value.slice(start, end);
          // 判断是否为跨行选择
          const hasNL = sel.includes('\n');
          if (!e.shiftKey) {
            if (!hasNL) {
              insertAtCursor(mdEditor, INDENT);
            } else {
              // 多行统一前置缩进
              const s = value.lastIndexOf('\n', start - 1) + 1;
              const eidx = value.indexOf('\n', end);
              const e2 = eidx === -1 ? value.length : eidx;
              const body = value.slice(s, e2);
              const replaced = body.split('\n').map(line => INDENT + line).join('\n');
              mdEditor.value = value.slice(0, s) + replaced + value.slice(e2);
              mdEditor.selectionStart = s;
              mdEditor.selectionEnd = s + replaced.length;
              updateMdStatus();
            }
          } else {
            // 反缩进：尽量去掉每行开头的两个空格
            const s = value.lastIndexOf('\n', start - 1) + 1;
            const eidx = value.indexOf('\n', end);
            const e2 = eidx === -1 ? value.length : eidx;
            const body = value.slice(s, e2);
            const replaced = body.split('\n').map(line => line.startsWith(INDENT) ? line.slice(INDENT.length) : line).join('\n');
            mdEditor.value = value.slice(0, s) + replaced + value.slice(e2);
            mdEditor.selectionStart = s;
            mdEditor.selectionEnd = s + replaced.length;
            updateMdStatus();
          }
        };

        // 监听编辑器事件
        mdEditor?.addEventListener('keydown', (e) => {
          if (e.key === 'Enter') return handleEnter(e);
          if (e.key === 'Tab') return handleTab(e);
        });
        mdEditor?.addEventListener('keyup', updateMdStatus);
        mdEditor?.addEventListener('click', updateMdStatus);
        mdEditor?.addEventListener('input', updateMdStatus);
        updateMdStatus();

        // ---------- 编辑输入 → 实时预览渲染（所见即所得） ----------
        let renderTimer = null;
        const renderPreview = () => {
          if (!mdPreview || !mdEditor) return;
          mdPreview.innerHTML = renderMarkdown(mdEditor.value || '');
        };
        const scheduleRender = () => {
          if (renderTimer) clearTimeout(renderTimer);
          renderTimer = setTimeout(() => { renderPreview(); }, 60); // 轻微防抖
        };
        // 首次渲染
        renderPreview();

        // ---------- 编辑区 ↔ 预览区 联动滚动 ----------
        let syncing = false; // 防止循环触发
        const syncScroll = (fromEl, toEl) => {
          if (!fromEl || !toEl) return;
          const fromMax = Math.max(1, fromEl.scrollHeight - fromEl.clientHeight);
          const ratio = fromEl.scrollTop / fromMax;
          const toMax = Math.max(1, toEl.scrollHeight - toEl.clientHeight);
          toEl.scrollTop = Math.round(ratio * toMax);
        };
        mdEditor?.addEventListener('scroll', () => {
          if (syncing) return;
          syncing = true;
          syncScroll(mdEditor, mdPreview);
          // 使用微任务/定时器解除同步锁
          setTimeout(() => { syncing = false; }, 0);
        });
        mdPreview?.addEventListener('scroll', () => {
          if (syncing) return;
          syncing = true;
          syncScroll(mdPreview, mdEditor);
          setTimeout(() => { syncing = false; }, 0);
        });

        // 输入联动渲染（编辑 → 预览）
        mdEditor?.addEventListener('input', scheduleRender);
      });
    </script>
  </body>
  </html>